﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PSE.AuditLogging.LogEntry;
using System.Messaging;
using System.Diagnostics;
using PSE.Framework.ProviderLoader;
using PSE.Framework.ProviderLoader.Configuration;
using System.Configuration;
using PSE.AuditLogging.Common.Exception;

namespace PSE.AuditLogging.Repository.Providers
{
    public class MessageQueueRepository : BaseProvider, ITemporaryRepository, IDisposable
    {
        MessageQueue _mq;


        public MessageQueueRepository(ProviderConfigurationElement configuration)
            : base(configuration)
        {
            try
            {
                string logQueueAddress;

                if (this.Configuration.Settings["queueAddress"] == null)
                    throw new ConfigurationErrorsException("The configuration key 'queueAddress' not found in the config file.");
                else
                {
                    logQueueAddress = this.Configuration.Settings["queueAddress"].Value;
                    if (string.IsNullOrEmpty(logQueueAddress))
                        throw new ConfigurationErrorsException("The configuration key 'queueAddress' is empty in the config file.");

                }

                _mq = new MessageQueue(logQueueAddress);
                _mq.Formatter = new BinaryMessageFormatter();
                _mq.DefaultPropertiesToSend.Recoverable = true;//indicar que todas as mensagens serao persistentes
            }
            catch (Exception ex)
            {
                throw new UnavailableRepositoryException(configuration.Name, ex);
            }
        }


        /// <summary>
        /// Enviar uma mensagem para fila
        /// </summary>
        /// <param name="entryPackage"></param>
        public void Send(LogEntryPackage entryPackage)
        {
            try
            {
                _mq.Send(entryPackage, entryPackage.Id);
            }
            catch (MessageQueueException ex)
            {///se ocorrer um erro de message queue
                throw new UnavailableRepositoryException(this.Configuration.Name, ex);
            }
        }

        /// <summary>
        /// Ler a primeira mensagem da fila, removendo-a
        /// </summary>
        /// <returns></returns>
        public LogEntryPackage Receive()
        {
            try
            {
                Message msg = _mq.Receive(new TimeSpan(0, 0, 0, 0, 10));

                if (msg != null)
                    return (LogEntryPackage)msg.Body;
                else
                    return null;
            }
            catch (InvalidCastException ex)
            {///se ocorrer um erro ao converter o objeto recuperado da fila
                //throw new InvalidLogEntryPackageException("Invalid log entry package. See the inner exception for more details.", ex);
                throw new InvalidLogEntryPackageException(ex);
            }
            catch (MessageQueueException ex)
            {
                if (ex.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)//nao ha mensagens
                    return null;
                else//se ocorrer um erro de message queue que nao seja o de timeout
                    throw new UnavailableRepositoryException(this.Configuration.Name, ex);
            }
        }

        /// <summary>
        /// Remover a primeira mensagem da fila
        /// </summary>
        public void RemoveTop()
        {
            try
            {
                Message msg = _mq.Receive(new TimeSpan(0, 0, 0, 0, 10));
            }
            catch (MessageQueueException ex)
            {
                //se ocorrer qualquer erro de fila que nao seja o que indica que nao ha mensagens
                if (ex.MessageQueueErrorCode != MessageQueueErrorCode.IOTimeout)
                    throw new UnavailableRepositoryException(this.Configuration.Name, ex);
            }
        }

        /// <summary>
        /// Ler a primeira mensagem da fila
        /// </summary>
        /// <returns></returns>
        public LogEntryPackage Peek()
        {
            try
            {
                Message msg = _mq.Peek(new TimeSpan(0, 0, 0, 0, 10));
                if (msg != null)
                    return (LogEntryPackage)msg.Body;
                else
                    return null;
            }
            catch (InvalidOperationException ex)
            {
                ///se ocorrer um erro ao converter o objeto recuperado da fila
                //throw new InvalidLogEntryPackageException("Invalid log entry package. See the inner exception for more details.", ex);
                throw new InvalidLogEntryPackageException(ex);
            }
            catch (InvalidCastException ex)
            {///se ocorrer um erro ao converter o objeto recuperado da fila
                //throw new InvalidLogEntryPackageException("Invalid log entry package. See the inner exception for more details.", ex);
                throw new InvalidLogEntryPackageException(ex);
            }
            catch (MessageQueueException ex)
            {
                if (ex.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)//indica q nao ha mensagens
                    return null;
                else//se ocorrer um erro de message queue que nao seja o de timeout
                    throw new UnavailableRepositoryException(this.Configuration.Name, ex);
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (_mq != null)
                _mq.Dispose();
        }

        #endregion
    }
}
